/*
 * Copyright (c) 2009, Servoy-stuff
 * 
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 * 
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
 * http://www.fsf.org/licensing/licenses/lgpl.txt
 */
package net.stuff.servoy.beans.dnd;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;

import com.servoy.j2db.IForm;
import com.servoy.j2db.IFormManager;
import com.servoy.j2db.Messages;
import com.servoy.j2db.plugins.IClientPluginAccess;
import com.servoy.j2db.scripting.IScriptObject;
import com.servoy.j2db.ui.IComponent;
import com.servoy.j2db.util.ComponentFactoryHelper;
import com.servoy.j2db.util.Debug;
import com.servoy.j2db.util.PersistHelper;

/**
 * @author Servoy Stuff
 * http://www.servoy-stuff.net/
 */
public class DnDFileChooser extends JPanel implements IComponent, IScriptObject, DropTargetListener, ActionListener {

	private static final long serialVersionUID = 1L;
	
	public static final String TOOLTIP_GET_BGCOLOR = "bgcolor";
	public static final String TOOLTIP_GET_FGCOLOR = "fgcolor";
	public static final String TOOLTIP_IS_VISIBLE = "visible";
	public static final String TOOLTIP_SET_BORDER = "border";
	public static final String TOOLTIP_SET_FONT = "font";
	public static final String TOOLTIP_TOOLTIP = "toolTipText";
	public static final String TOOLTIP_FILES = "getFiles";
	public static final String TOOLTIP_RESET = "reset";
	public static final String TOOLTIP_POPUPOMIT = "popupOmitString";
	public static final String TOOLTIP_RECURSE = "recurseFolders";
	
	private JMenuItem menuItem;
	private DefaultListModel model = new DefaultListModel();
	private JList liste = new JList(model);
	private IClientPluginAccess app;
	private String onDrop;
	private String onOmit;
	private String toolTipText;
	private String titleText;
	private String popupOmitString = "Omit";
	private boolean recurseFolders;
	private String accept;
	private List acceptableList;
	
	/* ---------------------------------------------------------------------------------------
	 * Initialization of the specialized behavior
	 ----------------------------------------------------------------------------------------- */
	public void init() {
		setLayout(new BorderLayout());
		
		setPreferredSize(new Dimension(300,300));
		
		if (isVisible()) {
			new DropTarget(liste, this);
		} else {
			if (app != null) {
				if (app.getCurrentWindow() != null && app.getCurrentWindow().getDropTarget() == null) {
					new DropTarget(app.getCurrentWindow(), this);
				}
			}
		}
		addPopupMenu();
		addKListeners();

		JScrollPane listScrollPane = new JScrollPane(liste);
		add(listScrollPane, BorderLayout.CENTER);
		
	}
	
	/**
	 * Will be called by the IServoyBeanFactory
	 * @param app the app to set
	 */
	public void setApp(IClientPluginAccess app) {
		this.app = app;
	}

		
	/* ---------------------------------------------------------------------------------------
	 * Methods that can be called by Servoy client
	 ----------------------------------------------------------------------------------------- */	
	public File[] js_getFiles() {
		File[] array = null;
		if (model != null && model.size() > 0) {
			array = new File[model.size()];
			for (int i = 0; i < model.size(); i++) {
				array[i] = (File) model.get(i);
			}
		}
		return (array == null) ? new File[0] : array;
	}
		
	public void js_reset() {
		if (model != null) {
			model.clear();
			repaint();
		}
	}
	
	/* ---------------------------------------------------------------------------------------
	 * String names of the callback methods
	 ----------------------------------------------------------------------------------------- */

	/**
	 * @return the onDrop
	 */
	public String getOnDrop() {
		return onDrop;
	}

	/**
	 * @param onDrop the onDrop to set
	 */
	public void setOnDrop(String onDrop) {
		this.onDrop = onDrop;
	}

	/**
	 * @return the onOmit
	 */
	public String getOnOmit() {
		return onOmit;
	}

	/**
	 * @param onOmit the onOmit to set
	 */
	public void setOnOmit(String onOmit) {
		this.onOmit = onOmit;
	}
	
	public String getToolTipText() {
		return toolTipText;
	}
	public String js_getToolTipText() {
		return getToolTipText();
	}
	
	/**
	 * @param toolTipText the toolTipText to set
	 */
	public void setToolTipText(String toolTipText) {
		this.toolTipText = toolTipText;
		if (liste != null) {
			liste.setToolTipText(Messages.getStringIfPrefix(toolTipText));
		}
	}
	
	public void js_setToolTipText(String toolTipText) {
		setToolTipText(toolTipText);
	}

	/**
	 * @return the popupOmitString
	 */
	public String getPopupOmitString() {
		if (popupOmitString == null) {
			popupOmitString = "Omit";
		}
		return Messages.getStringIfPrefix(popupOmitString);
	}
	
	public String js_getPopupOmitString() {
		return getPopupOmitString();
	}

	/**
	 * @param popupOmitString the popupOmitString to set
	 */
	public void setPopupOmitString(String popupOmitString) {
		this.popupOmitString = popupOmitString;
		if (menuItem != null) {
			menuItem.setText(Messages.getStringIfPrefix(popupOmitString));
		}
	}
	public void js_setPopupOmitString(String popupOmitString) {
		setPopupOmitString(popupOmitString);
	}
	
	/**
	 * @return the recurseFolders
	 */
	public boolean isRecurseFolders() {
		return this.recurseFolders;
	}
	public boolean js_isRecurseFolders() {
		return isRecurseFolders();
	}

	/**
	 * @param recurseFolders the recurseFolders to set
	 */
	public void setRecurseFolders(boolean recurseFolders) {
		this.recurseFolders = recurseFolders;
	}
	public void js_setRecurseFolders(boolean recurseFolders) {
		setRecurseFolders(recurseFolders);
	}

	/* (non-Javadoc)
	 * @see javax.swing.JComponent#setForeground(java.awt.Color)
	 */
	public void setForeground(Color foreground) {
		if (liste != null) {
			liste.setForeground(foreground);
		}
	}
	public void js_setFgcolor(String paramString) {
		setForeground(PersistHelper.createColor(paramString));
	}
	public String js_getFgcolor() {
		return (getForeground() == null) ? null : PersistHelper.createColorString(getForeground());
	}
	
	/* (non-Javadoc)
	 * @see javax.swing.JComponent#setBackground(java.awt.Color)
	 */
	public void setBackground(Color background) {
		if (liste != null) {
			liste.setBackground(background);
		}
	}
	public void js_setBgcolor(String paramString) {
		setBackground(PersistHelper.createColor(paramString));
	}
	public String js_getBgcolor() {
		return (getBackground() == null) ? null : PersistHelper.createColorString(getBackground());
	}
		
	/* (non-Javadoc)
	 * @see javax.swing.JComponent#setFont(java.awt.Font)
	 */
	public void setFont(Font font) {
		if (liste != null) {
			liste.setFont(font);
		}
	}
	public void js_setFont(String paramString) {
		if (paramString != null) {
			setFont(PersistHelper.createFont(paramString));
		}
	}
	public String js_getFont() {
		return (getFont() == null) ? null : PersistHelper.createFontString(getFont());
	}
	
	
	public void setVisible(boolean b) {
		super.setVisible(b);
		if (b) {
			if (app != null && app.getCurrentWindow().getDropTarget() != null) {
				app.getCurrentWindow().setDropTarget(null);
			}
			if (liste != null && liste.getDropTarget() == null) {
				new DropTarget(liste, this);
			}
		} else {
			if (liste != null && liste.getDropTarget() != null) {
				liste.setDropTarget(null);
			}
			if (app != null && app.getCurrentWindow().getDropTarget() == null) {
				new DropTarget(app.getCurrentWindow(), this);
			}
		}
	}
	public void js_setVisible(boolean b) {
		setVisible(b);
	}
	public boolean js_isVisible() {
		return isVisible();
	}
		
	public void js_setBorder(String paramString) {
		setBorder(ComponentFactoryHelper.createBorder(paramString));
	}
	public String js_getBorder() {
		return (getBorder() == null) ? null : ComponentFactoryHelper.createBorderString(getBorder());
	}
	
	/**
	 * @return the titleText
	 */
	public String getTitleText() {
		return this.titleText;
	}

	/**
	 * @param titleText the titleText to set
	 */
	public void setTitleText(String titleText) {
		this.titleText = titleText;
	}
	
	/**
	 * @return the accept
	 */
	public String getAccept() {
		return this.accept;
	}
	
	/**
	 * @param accept
	 */
	public void setAccept(String accept) {
		this.accept = accept;
		this.acceptableList = new ArrayList();
		if (accept != null && accept.trim().length() > 0) {
			String[] acceptables = accept.split(",|;|\\s");
			if (acceptables != null && acceptables.length > 0) {
				for (int i = 0; i < acceptables.length; i++) {
					acceptableList.add(acceptables[i].trim().toLowerCase());
				}
			}
		}
	}


	/* ---------------------------------------------------------------------------------------
	 * DropTargetListener implementation
	 ----------------------------------------------------------------------------------------- */

	/* (non-Javadoc)
	 * @see java.awt.dnd.DropTargetListener#dropActionChanged(java.awt.dnd.DropTargetDragEvent)
	 */
	public void dropActionChanged(DropTargetDragEvent dtde) {
		// ignore
	}

	/* (non-Javadoc)
	 * @see java.awt.dnd.DropTargetListener#dragEnter(java.awt.dnd.DropTargetDragEvent)
	 */
	public void dragEnter(DropTargetDragEvent dtde) {
		// ignore
	}

	/* (non-Javadoc)
	 * @see java.awt.dnd.DropTargetListener#dragExit(java.awt.dnd.DropTargetEvent)
	 */
	public void dragExit(DropTargetEvent dte) {
		// ignore
	}

	/* (non-Javadoc)
	 * @see java.awt.dnd.DropTargetListener#dragOver(java.awt.dnd.DropTargetDragEvent)
	 */
	public void dragOver(DropTargetDragEvent dtde) {
		// ignore
	}
	
	/* (non-Javadoc)
	 * @see java.awt.dnd.DropTargetListener#drop(java.awt.dnd.DropTargetDropEvent)
	 */
	public void drop(DropTargetDropEvent dtde) {
		try {
			// OK, get the dropped object and try to figure out what it is
			Transferable tr = dtde.getTransferable();
			DataFlavor[] flavors = tr.getTransferDataFlavors();
			for (int i = 0; i < flavors.length; i++) {
			Debug.log("Possible flavor: " + flavors[i].getMimeType());
			
	    	// Check for file lists specifically
			if (flavors[i].isFlavorJavaFileListType()) {
				  // Great!  Accept copy drops...
				  dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);

				  // Add the list of file names to our model
				  java.util.List list = (java.util.List)tr.getTransferData(flavors[i]);
				  for (int j = 0; j < list.size(); j++) {
					  File f = (File) list.get(j);
					  if (!model.contains(f)) {
						  if (isRecurseFolders() && f.isDirectory()) {
							  addFolder(f);
						  } else {
							  if (isAccepted(f)) model.addElement(f);
						  }
					  }
				  }

				  // If we made it this far, everything worked.
				  dtde.dropComplete(true);
				  repaint();
				  fireOnDrop();
				  return;
			  }
		  // OK, is it another Java object?
			  else if (flavors[i].isFlavorSerializedObjectType()) {
				  dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);

				  Object o = tr.getTransferData(flavors[i]);
				  String[] s = o.toString().split("\\n");
				  for (int j = 0; j < s.length; j++) {
					  String f = s[j].toString();
					  File fl = new File(f);
					  if (fl != null && fl.exists()) {
						  if (!model.contains(fl)) {
							  if (isRecurseFolders() && fl.isDirectory()) {
								  addFolder(fl);
							  } else {
								  if (isAccepted(fl)) model.addElement(fl);
							  }
						  }
					  }
				  }
				  
				  dtde.dropComplete(true);
				  repaint();
				  fireOnDrop();
				  return;
			  }
		  // How about an input stream?
			  else if (flavors[i].isRepresentationClassInputStream()) {
				  dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
				  InputStream is = (InputStream)tr.getTransferData(flavors[i]);
				  BufferedReader reader = new BufferedReader(new InputStreamReader(is)); 
				  String line = null; 
				  while ((line = reader.readLine()) != null) {
					  File fl = new File(line);
					  if (fl != null && fl.exists()) {
						  if (!model.contains(fl)) {
							  if (isRecurseFolders() && fl.isDirectory()) {
								  addFolder(fl);
							  } else {
								  if (isAccepted(fl)) model.addElement(fl);
							  }
						  }
					  }
				  } 
				  is.close();
				  dtde.dropComplete(true);
				  repaint();
				  fireOnDrop();
				  return;
			  }
	      }
	      // Well, the user must not have dropped a file list
	      Debug.log("Drop failed: " + dtde);
	      dtde.rejectDrop();
			    
		} catch (Exception e) {
			Debug.log("Drop error:" + e.getLocalizedMessage());
			dtde.rejectDrop();
		}
	}

	/**
	 * Add content of folder recursively
	 * @param f File
	 */
	private void addFolder(File f) {
		if (f.isDirectory()) {
			File[] files = f.listFiles();
			if (files != null && files.length > 0) {
				for (int i = 0; i < files.length; i++) {
					if (files[i].isDirectory()) {
						addFolder(files[i]);
					} else {
						if (!model.contains(files[i])&& isAccepted(f)) {
							model.addElement(files[i]);
						}
					}
				}
			}
		}
		
	}
	
	

	/* ---------------------------------------------------------------------------------------
	 * ActionListener implementation
	 ----------------------------------------------------------------------------------------- */
	/* (non-Javadoc)
	 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
	 */
	public void actionPerformed(ActionEvent e) {
		JMenuItem source = (JMenuItem)(e.getSource());
		if (source.getText() == getPopupOmitString()) {
			removeSelected();
		}
	}


	/* ---------------------------------------------------------------------------------------
	 * IComponent implementation
	 ----------------------------------------------------------------------------------------- */
	
	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IComponent#getId()
	 */
	public String getId() {
		return null;
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IComponent#setComponentEnabled(boolean)
	 */
	public void setComponentEnabled(boolean b) {
		setEnabled(b);
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.ui.IComponent#setComponentVisible(boolean)
	 */
	public void setComponentVisible(boolean b) {
		setVisible(b);
	}
	
	/* ---------------------------------------------------------------------------------------
	 * IScriptObject implementation
	 ----------------------------------------------------------------------------------------- */
	
	/* (non-Javadoc)
	 * @see com.servoy.j2db.scripting.IScriptObject#getAllReturnedTypes()
	 */
	public Class[] getAllReturnedTypes() {
		return null;
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.scripting.IScriptObject#getParameterNames(java.lang.String)
	 */
	public String[] getParameterNames(String methodName) {
		if (TOOLTIP_SET_BORDER.equals(methodName)) {
			return new String[] {"border"};
		} else if (TOOLTIP_SET_FONT.equals(methodName)) {
			return new String[] {"font"};
		}
		return null;
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.scripting.IScriptObject#getSample(java.lang.String)
	 */
	public String getSample(String methodName) {
		StringBuffer buff = new StringBuffer();
		String tooltip = getToolTipFor(methodName);
		if (tooltip != null) {
			buff.append("//");
			buff.append(tooltip);
			buff.append("\n");
			if (TOOLTIP_GET_BGCOLOR.equals(methodName)) {
				buff.append("\tvar currentbg = %%elementName%%.bgcolor;\n");
				buff.append("\t%%elementName%%.bgcolor = currentbg;");
			} else if (TOOLTIP_GET_FGCOLOR.equals(methodName)) {
				buff.append("\tvar currentfg = %%elementName%%.fgcolor;\n");
				buff.append("\t%%elementName%%.fgcolor = currentfg;");
			} else if (TOOLTIP_IS_VISIBLE.equals(methodName)) {
				buff.append("\tvar currentState = %%elementName%%.visible;\n");
				buff.append("\t%%elementName%%.visible = !currentState;");				
			} else if (TOOLTIP_SET_BORDER.equals(methodName)) {
				buff.append("\t%%elementName%%.setBorder('LineBorder,1,#ccffcc');");
			} else if (TOOLTIP_TOOLTIP.equals(methodName)) {
				buff.append("\tvar txt = %%elementName%%.toolTipText;\n");
				buff.append("\t%%elementName%%.toolTipText = txt;");
			} else if (TOOLTIP_SET_FONT.equals(methodName)) {
				buff.append("\t%%elementName%%.setFont('Tahoma,1,11');");
			} else if (TOOLTIP_FILES.equals(methodName)) {
				buff.append("\t\n\t//the content of the array will also depend on the value of the recurseFolders flag\n");
				buff.append("\t//if true, will contain the files contained in each folder dropped\n");
				buff.append("\t//if false, will contain the folders dropped themselves\n");
				buff.append("\t\n\tvar array = %%elementName%%.getFiles();\n");
				buff.append("\tfor (var i = 0; i < array.length; i++)\n");
				buff.append("\t{\n");
				buff.append("\t\tapplication.output(array[i].getCanonicalPath());\n");
				buff.append("\t}");
			} else if (TOOLTIP_RESET.equals(methodName)) {
				buff.append("\t%%elementName%%.reset();");
			} else if (TOOLTIP_POPUPOMIT.equals(methodName)) {
				buff.append("\t%%elementName%%.popupOmitString = 'Clear';");
			} else if (TOOLTIP_RECURSE.equals(methodName)) {
				buff.append("\t%%elementName%%.recurseFolders = true;");
			}
			buff.append("\n\t");
			return buff.toString();
		}
		return null;
	}
	
	/**
	 * Utility method to return different tooltip depending on method name
	 * @param methodName
	 * @return
	 */
	private String getToolTipFor(String methodName) {
		if (TOOLTIP_SET_BORDER.equals(methodName)) {
			return "The border attributes must be the same as borderType string from Element Properties";
		} else if (TOOLTIP_SET_FONT.equals(methodName)) {
			return "The font 'fontname,style,size',  style = (plain=0, bold=1, italic=2, bold-italic=3)";
		}
		return getToolTip(methodName);
	}


	/* (non-Javadoc)
	 * @see com.servoy.j2db.scripting.IScriptObject#getToolTip(java.lang.String)
	 */
	public String getToolTip(String methodName) {
		if (TOOLTIP_GET_BGCOLOR.equals(methodName)) {
			return "Set / Get the background color"; 
		} else if (TOOLTIP_GET_FGCOLOR.equals(methodName)) {
			return "Set / Get the foreground color"; 
		} else if (TOOLTIP_IS_VISIBLE.equals(methodName)) {
			return "Set / Get the visiblity - if false the whole form background will become a drop target!"; 			
		} else if (TOOLTIP_SET_BORDER.equals(methodName)) {
			return "Set / Get the border";
		} else if (TOOLTIP_TOOLTIP.equals(methodName)) {
			return "Set / Get the toolTipText (when hovering over with mouse)";
		} else if (TOOLTIP_SET_FONT.equals(methodName)) {
			return "Set / Get the font";
		} else if (TOOLTIP_FILES.equals(methodName)) {
			return "Returns an Array of java.io.File for each file that has been dropped";
		} else if (TOOLTIP_RESET.equals(methodName)) {
			return "Clears the list of files already dropped";
		} else if (TOOLTIP_POPUPOMIT.equals(methodName)) {
			return "Set / Get the text of the \"omit\" popup viewed on right click in the list";
		} else if (TOOLTIP_RECURSE.equals(methodName)) {
			return "Set / Get the recurseFolders behavior - if true, will search recursively inside each folder dropped to retrieve all contained files";
		}
		return null;
	}

	/* (non-Javadoc)
	 * @see com.servoy.j2db.scripting.IScriptObject#isDeprecated(java.lang.String)
	 */
	public boolean isDeprecated(String methodName) {
		return false;
	}

	
	/* ---------------------------------------------------------------------------------------
	 * Private methods
	 ----------------------------------------------------------------------------------------- */
	
	/**
	 * Remove the selected files (called from key event or popupmenu
	 */
	private void removeSelected() {
		int[] selected = liste.getSelectedIndices();
		if (selected != null) {
			Object[] files = liste.getSelectedValues();
			for (int i = selected.length-1; i >= 0; i--) {
				model.remove(selected[i]);
			}
			fireOnOmit(files);
		}
		repaint();
	}
	
	
	/**
	 * Add the key listeners used to remove the selection
	 */
	private void addKListeners() {
		liste.addKeyListener(new KeyAdapter() {

			/* (non-Javadoc)
			 * @see java.awt.event.KeyAdapter#keyTyped(java.awt.event.KeyEvent)
			 */
			public void keyTyped(KeyEvent e) {
				if (e.getKeyChar() == '-') {
					removeSelected();
				}
			}

			/* (non-Javadoc)
			 * @see java.awt.event.KeyAdapter#keyReleased(java.awt.event.KeyEvent)
			 */
			public void keyReleased(KeyEvent e) {
				if (e.getKeyCode() == KeyEvent.VK_DELETE ||
						e.getKeyCode() == KeyEvent.VK_BACK_SPACE ||
						e.getKeyCode() == KeyEvent.VK_CLEAR ||
						e.getKeyCode() == KeyEvent.VK_DELETE) {
					removeSelected();
				}
			}
			
		});
	}

	/**
	 * Add a private popup menu
	 */
	private void addPopupMenu() {
		
        //Create the popup menu.
		JPopupMenu popup = new JPopupMenu();
        
        menuItem = new JMenuItem(getPopupOmitString());
        menuItem.addActionListener(this);
        popup.add(menuItem);

        //Add listener to the text area so the popup menu can come up.
        MouseListener popupListener = new PopupListener(popup);
        liste.addMouseListener(popupListener);
	}
	
	/**
	 * package protected mouse listener for the popup menu
	 */
	class PopupListener extends MouseAdapter {
        JPopupMenu popup;

        PopupListener(JPopupMenu popupMenu) {
            popup = popupMenu;
        }

        public void mousePressed(MouseEvent e) {
        	super.mousePressed(e);
            maybeShowPopup(e);
        }

        public void mouseReleased(MouseEvent e) {
        	super.mouseReleased(e);
            maybeShowPopup(e);
        }

        private void maybeShowPopup(MouseEvent e) {
            if (e.isPopupTrigger()) {
                popup.show(e.getComponent(),
                           e.getX(), e.getY());
            }
        }
    }

	
	/**
	 * Calls the onDrop JavaScript method callback (if any)
	 * With an array of the files as arguments
	 */
	private void fireOnDrop() {
		if (getOnDrop() != null) {
			runFormFunctionByName(getOnDrop(), js_getFiles());
			if (app != null && app.getCurrentWindow() != null) {
				app.getCurrentWindow().repaint();
			}
		}
	}
	
	/**
	 * Calls the onDelete JavaScript method callback (if any)
	 * arguments[0] will be an array of omitted files
	 * argumenst[1] will be the remaining files
	 */
	private void fireOnOmit(Object[] deleted) {
		if (getOnOmit() != null) {
			runFormFunctionByName(getOnOmit(), new Object[] {deleted, js_getFiles()});
			if (app != null && app.getCurrentWindow() != null) {
				app.getCurrentWindow().repaint();
			}
		}
	}

	/**
	 * Generic helper callback function
	 * @param functionName
	 * @param app
	 * @param params
	 */
	public void runFunctionByName(String functionName, Object[] params) {
		if (functionName != null) {
			String fName = functionName.trim();
			if (fName.length() > 0) {
				boolean global = fName.toLowerCase().startsWith("globals.");
				if (global) {
					runGlobalFunctionByName(fName, params);
				} else {
					runFormFunctionByName(fName, params);
				}
			}
		}
	}
	
	/**
	 * Generic callback to a "global" Function
	 * @param fName
	 * @param app
	 * @param params
	 */
	private void runGlobalFunctionByName(String fName, Object[] params) {
		try {
			fName = fName.substring("globals.".length());
			app.executeMethod(null, fName, params, true);
		} catch (Exception ex) {
			Debug.error(ex);
		}
	}
	
	
	/**
	 * Generic callback to a "form" Function
	 * @param fName
	 * @param app
	 * @param params
	 */
	private void runFormFunctionByName(String fName, Object[] params) {
		IFormManager manager = app.getFormManager();
		if (manager != null) {
			IForm form = manager.getCurrentForm();
			if (form != null) {
				String formName = form.getName();
				if (formName != null) {
					try {
						app.executeMethod(formName, fName, params, true);
					} catch (Exception ex) {
						Debug.error(ex);
					}
				}
			}
		}
	}
	
	private boolean isAccepted(File f) {
		if (acceptableList != null && acceptableList.size() > 0) {
			String name = f.getName();
			String[] parts = name.split("\\.");
			if (parts != null && parts.length > 0) {
				String extension = parts[parts.length-1].toLowerCase();
				return acceptableList.contains(extension);
			}
		}
		return true;
	}
	
	/* ---------------------------------------------------------------------------------------
	 * Standalone test method
	 ----------------------------------------------------------------------------------------- */

	/**
	 * Standalone test main method - used out of Servoy context to test the component
	 * Output file names to console when test button clicked
	 * @param args null
	 */
	public static void main(String[] args) {

		JFrame myFrame = new JFrame("Dnd Test");
		myFrame.setLayout(new BorderLayout());
		myFrame.setSize(300, 320);
		
		final DnDFileChooser chooser = new DnDFileChooser();
		chooser.init();

		myFrame.add(chooser, BorderLayout.CENTER);
		
		JButton button = new JButton("test");
		button.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				File[] files = chooser.js_getFiles();
				if (files != null && files.length > 0) {
					for (int i = 0; i < files.length; i++) {
						System.out.println(files[i].getAbsolutePath());
					}
				}
			}
			
		});
		myFrame.add(button, BorderLayout.SOUTH);
	
		myFrame.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				System.exit(0);
			}
		});
		
		myFrame.pack();
		myFrame.setVisible(true);
    }
	
}
